<template>
  <div style="height: 100vh; width: 100vw; background-color: #ededed">
    <div class="wrap">
      <!-- 头部 -->
      <div class="titleBox">
        <div style="cursor: pointer;display: flex;align-items: center;" @click="backPage">
          <img src="https://wld001.oss-cn-nanjing.aliyuncs.com/file/2024/04/28/3385b9a7-6caa-412e-ad04-59721e1b606d.png"
            alt="" class="head_portrait" style="margin-left: 20px; margin-right: 20px" />
          <span style="color: #fff">官方客服</span>
        </div>


        <i class="el-icon-s-tools set" @click="getSetting"></i>

        <!-- 在线状态弹框 -->
        <el-popover placement="bottom" trigger="click">
          <div class="stateBox2" @click="uploadState(1)">
            <span class="state1"></span>
            <span class="stateText">在线</span>
          </div>
          <div class="stateBox2" @click="uploadState(2)">
            <span class="state2"></span>
            <span class="stateText">离线</span>
          </div>
        </el-popover>


      </div>
      <!-- 底部 -->
      <div class="infoBox" v-if="goType == 1">
        <!-- 左边用户列表 -->
        <div class="userList">
          <div class="searchBox">
            <el-input placeholder="请输入内容" v-model="search" class="input-with-select" size="mini" @input="inquire">
              <i class="el-icon-search el-input__icon" slot="suffix" @click="handleIconClick">
              </i>
            </el-input>
            <el-button icon="el-icon-plus" size="mini" type="primary" @click="dialogVisible = true"></el-button>
          </div>
          <div class="userListBox">

            <el-collapse v-model="activeNames" @change="handleChange" class="foldingPanel" style="color: red;">

              <el-collapse-item title="待接入" name="1">
                <div
                  style="position: relative;display: flex;align-items: center;flex-wrap: wrap;justify-content: flex-start;box-sizing: border-box;padding: 0 10px;">


                  <div v-for="(item, index) in unUserListData" :key="index" style="margin-right: 14px;">
                    <el-popover placement="top-start" title="历史会话" width="50" trigger="hover" :content="item.unMessage">
                      <div :class="item.openId == act ? 'userFlexAct' : 'userFlex'" slot="reference"
                        @click="getAct(item, index, '1')">
                        <img
                          src="https://wld001.oss-cn-nanjing.aliyuncs.com/file/2024/06/03/93e296bb-5409-49a8-ad59-ff06d79327d8.png"
                          alt="头像" class="head_portrait2" />

                      </div>
                    </el-popover>

                  </div>


                </div>
              </el-collapse-item>

              <el-collapse-item title="已接入" name="2">
                <div style="position: relative;" v-for="(item, index) in userListData" :key="index"
                  @click="getAct(item, index, '2')" @contextmenu.prevent="onContextmenu($event, item, index)">


                  <div :class="item.openId == act ? 'userFlexAct' : 'userFlex'">
                    <div>
                      <img
                        src="https://wld001.oss-cn-nanjing.aliyuncs.com/file/2024/06/03/93e296bb-5409-49a8-ad59-ff06d79327d8.png"
                        alt="头像" class="head_portrait2" style="margin-left: 20px;margin-right: 10px;" />
                    </div>
                    <div style="margin-right: 0px;display: flex;flex-wrap: wrap;width: 100%;">
                      <el-tooltip :content="item.phone" placement="bottom" effect="light">
                        <div style="color: #565656" class="userName">
                          网友 <span style="margin-left: 12px;">{{ item.lastTime }}</span>
                        </div>
                      </el-tooltip>
                      <div class="userInfo">
                        <!-- 最后一条信息 -->
                        {{ item.lastMessage }}
                      </div>
                    </div>
                    <div style="margin-right: 10px; font-size: 14px; color: #ccc">
                      {{ item.timer }}
                    </div>

                  </div>
                  <el-badge :value="item.unreadQuantity" :max="99" v-if="item.unreadQuantity != 0"
                    class="badgeItem"></el-badge>

                </div>
              </el-collapse-item>

            </el-collapse>


            <!-- 通过右击获取到的坐标定位 -->
            <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="newcontextmenu">
              <li @click="addDepartment('新增下级')"><i class="el-icon-plus" style="color:#1e9fff"></i> 新增下级</li>
              <li @click="addDepartment('新增同级')"><i class="el-icon-plus" style="color:#1e9fff"></i>新增同级</li>
            </ul>

          </div>
        </div>
        <!-- 右边输入框和信息展示 -->
        <div class="infoList">

          <!-- 当前会话人信息 -->
          <div class="userInfos">
            <div style="font-size: 15px;color: #fff;display: flex;">

              <div v-if="currentConversationPerson.channel == 1">卫莱电微信</div>
              <div v-if="currentConversationPerson.channel == 2">卫莱电支付宝</div>
              <div v-if="currentConversationPerson.channel == 3">阿凡电微信</div>
              <div v-if="currentConversationPerson.channel == 4">阿凡电支付宝</div>
              <div v-if="currentConversationPerson.channel == 5">浏览器</div>

            </div>
            <div style="font-size: 14px;color: #fff;margin-top: 4px;">{{ currentConversationPerson.phone || '没得' }}
            </div>
          </div>

          <!-- 信息 -->
          <div class="infoTop" ref="scrollBox" id="box">
            <div :class="item.type == 1 ? 'chatInfoLeft' : 'chatInfoRight'
          " v-for="(item, index) in userInfoList" :key="index">


              <img
                :src="item.type == 1 ? 'https://wld001.oss-cn-nanjing.aliyuncs.com/file/2024/06/04/53867032-a217-4539-9af3-a61e12e19fd3.jpeg' : 'https://wld001.oss-cn-nanjing.aliyuncs.com/file/2024/06/04/ecdfe3c5-40a2-4b84-9056-bd16e4e2545c.jpg'"
                alt="头像" class="head_portrait2" />
              <div :class="item.type == 1 ? 'chatLeft' : 'chatRight'">

                <div class="text" v-html="item.message"></div>
              </div>
            </div>
          </div>
          <!-- 输入框 -->
          <div class="infoBottom">
            <div class="infoIcon">

              <el-upload class="upload-demo" action="/bussiness/user/addFile " :headers="uploadHeaders"
                :show-file-list="false" :on-success="handleAvatarSuccess">
                <i class="el-icon-picture-outline-round"></i>
              </el-upload>


              <i @click="extend('发送商品')" class="el-icon-sell"></i>
              <i @click="getSetting('设置')" class="el-icon-setting"></i>
              <i @click="extend('聊天记录')" class="el-icon-chat-dot-round"></i>
              <i @click="extend('更多选项')" class="el-icon-more-outline"></i>
            </div>
            <textarea type="textarea" class="infoInput" v-model="textarea"
              @keydown.enter.exact="handlePushKeyword($event)" @keyup.ctrl.enter="lineFeed"
              :disabled="isshow == 1 && currentConversationPerson.isDisplay == 1 ? false : true" />
            <div class="fasong" @click="setUp" v-show="isshow == 1 ? true : false">
              发送
            </div>
          </div>
        </div>
      </div>


      <!-- 设置页面 ，可设置开场白和默认 -->
      <Setting ref="Setting" v-if="goType == 2" @open="openDialog" />
      <!-- <editorBar v-model="model.main_content" :isClear="false" ref='editEditorBar'></editorBar> -->

    </div>

    <!-- 添加修改 -->
    <el-dialog :title="dialogTitle" :visible.sync="dialog" top="5vh" :before-close="cancel"
      :modal-append-to-body="false" :close-on-click-modal="true" :close-on-press-escape="true">
      <el-form ref="ref" :model="model" label-width="100px" class="dialog-body">

        <el-form-item label="问题" prop="problem">
          <el-input v-model="model.problem" placeholder="请输入问题" clearable />
        </el-form-item>

        <el-form-item label="排序" prop="sort">
          <el-input v-model="model.sort" placeholder="请输入序号(如1,2,3)" clearable />
        </el-form-item>

        <el-form-item label="问题解答" prop="answer">
          <editorBar v-model="model.answer" :isClear="false" :value="model.answer" ref='EditEditorBar'></editorBar>
        </el-form-item>


      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button :loading="loading" @click="cancel">取消</el-button>
        <el-button :loading="loading" type="primary" @click="submit">提交</el-button>
      </div>
    </el-dialog>


    <!-- 搜索框边 + 号弹框 -->
    <el-dialog title="选择需要添加的联系人" :visible.sync="dialogVisible" width="30%" :modal="false">
      <span>自定义页面，还没想好写什么功能</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
// import WebSocket from 'websocket';
import { timeDifference } from '@/utils/format'
import Setting from './setting.vue'
import editorBar from '@/components/editorBar'

export default {
  components: {
    Setting,
    editorBar
  },
  data() {
    return {
      model: {},
      //websocket部分
      path: "ws://192.168.2.7:8615/customerServiceWebSocket", //后台的websocket地址，找后端要
      ws: null, //建立的连接
      lockReconnect: false, //是否真正建立连接
      timeout: 10 * 1000, //30秒一次心跳
      timeoutObj: null, //心跳心跳倒计时
      serverTimeoutObj: null, //心跳倒计时
      timeoutnum: null, //断开 重连倒计时

      // 在线状态
      state: 1,
      //搜索用户
      search: "",
      //用户列表渲染数据
      userListData: [],
      //用户列表筛选数据
      userListDatas: [],
      //用户点击选中变色
      act: Number,
      // 加号弹框
      dialogVisible: false,
      //历史信息
      userInfoList: [],
      // 当前会话框人数据
      currentConversationPerson: {},
      //输入框
      textarea: "",
      //滚动条距离顶部距离
      scrollTop: 0,
      //发送和输入显隐
      isshow: 0,
      socket: '',
      userQuery: {
        page: 1,
        limit: 15
      },
      activeNames: ['1', '2'], // 展开指定项

      unUserListData: [],
      uploadHeaders: {},

      currentSessionPage: {
        page: 1,
        limit: 12,
        more: true
      },
      toBeAccessed: '',
      visible: false,
      top: 0,
      left: 0,
      contextMenuData: [

      ],

      goType: 1,
      dialog: false,
      dialogTitle: '',
      model: {},
      loading: false,


    };
  },
  created() {

    const uploadHeaders = {};
    let token = localStorage.getItem('CustomerToken');
    uploadHeaders['CustomerToken'] = JSON.parse(token);
    this.uploadHeaders = uploadHeaders;

    this.initWebpack();

    // 待接入连接
    this.connectToBeConnected()

  },
  mounted() {
    this.getHasBeenConnectedTo()

    this.getSessionToBeConnectedList()
  },
  beforeDestroy() {
    // 离开页面后关闭连接
    this.ws.close();
    // 清除时间
    clearTimeout(this.timeoutObj);
    clearTimeout(this.serverTimeoutObj);
  },
  methods: {

    async submit() {
      if (!this.model.problem) {
        this.$message.error('请先输入问题')
        return false
      }

      if (!this.model.sort) {
        this.$message.error('请先输入排序')
        return false
      }

      if (!this.model.answer) {
        this.$message.error('请先问题解答')
        return false
      }
      this.model.type = 2
      if (this.dialogTitle == '添加问题') {
        let res = await this.$api.frequentlyAskedQuestions_add(this.model)
        if (res.code == 200) {
          this.$message.success(res.msg)
          this.dialog = false
          this.$refs.Setting.getList()
        } else {
          this.$message.error(res.msg)
        }
      }else{ 
        let res = await this.$api.frequentlyAskedQuestions_edit(this.model)
        if (res.code == 200) {
          this.$message.success(res.msg)
          this.dialog = false
          this.$refs.Setting.getList()
        } else {
          this.$message.error(res.msg)
        }
      }


    },

    cancel() {
      this.dialog = false
    },

    // 打开弹窗
    openDialog(title, row = false) {
      this.dialogTitle = title
      this.dialog = true
      if (row) {
          this.model.problem = JSON.parse(JSON.stringify(row.problem))
          this.model.sort = JSON.parse(JSON.stringify(row.sort))
          this.model.frequentlyAskedQuestionsId= row.frequentlyAskedQuestionsId
        this.$nextTick(()=>{
          console.log(this.$refs.EditEditorBar);
          this.$refs.EditEditorBar.valueHtml(row.answer)
        })


      }
    },
    // 返回聊天
    backPage() {
      this.goType = 1
    },

    // 去设置页面
    async getSetting() {
      this.goType = 2
      this.$nextTick(() => {
        this.$refs.Setting.getList()
      })
    },

    // 结束会话
    async endSession(item, index) {
      let pem = {}
      pem.openId = item.openId
      let res = await this.$api.user_endSession(pem)
      if (res.code == 200) {

        this.userListData[index].isDisplay = 2

        this.$message.success(res.msg)
      } else {
        this.$message.error(res.msg)
      }
    },


    //鼠标右键事件
    onContextmenu(e, data, index) {
      this.$contextmenu({
        items: [
          {
            label: "结束会话",
            icon: "el-icon-close",
            onClick: () => {
              this.endSession(data, index);
            },
          },
          {
            label: "移入黑名单",
            icon: "el-icon-delete-solid",
            onClick: () => {
              this.moveIntoBlacklist(data, index);
            },
          }
        ],
        event, // 鼠标事件信息
        customClass: 'custom-class', // 自定义菜单 class
        zIndex: 3, // 菜单样式 z-index
        minWidth: 230 // 主菜单最小宽度
      });
      return false;
    },

    // 发送图片成功
    handleAvatarSuccess(val) {
      console.log(val);
      let jsonObj = JSON.stringify({
        message: `<img
                :src="${val.msg}"
                alt="头像" class="head_portrait2" />` ,
        customerServiceAccount: JSON.parse(localStorage.getItem('accountNumber')),
        openId: this.currentConversationPerson.openId
      });
      this.websocketsend(jsonObj);


      this.userInfoList.push({ message: val.msg })

      console.log("发送内容:", this.textarea);
      this.textarea = "";
      // 页面滚动到底部
      this.$nextTick(() => { // 一定要用nextTick
        this.setPageScrollTo();
        //页面滚动条距离顶部高度等于这个盒子的高度
        this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
      })
    },
    // 展开会话
    handleChange() { },
    //切换客服状态
    uploadState(state) {
      if (state !== 4) {
        this.state = state;
      } else {
        this.$confirm("是否退出登录?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            this.$message({
              type: "success",
              message: "退出成功!",
            });
          })
          .catch(() => {
            this.$message({
              type: "info",
              message: "已取消退出",
            });
          });
      }
    },

    // 获取待接入的会话
    async getSessionToBeConnectedList() {
      let res = await this.$api.user_userNotConnected(this.userQuery)

      this.unUserListData = res.data.map(item => {
        item.unMessage = ""
        item.lastTime = timeDifference(item.lastMessageTime)
        return item
      })

      let pem = {
        page: 1,
        limit: 3
      }
      for (let l = 0; l < this.unUserListData.length; l++) {
        pem.openId = this.unUserListData[l].openId
        let res = await this.$api.information_list(pem)
        this.unUserListData[l].unMessage = []
        for (let o = 0; o < res.data.list.length; o++) {

          this.unUserListData[l].unMessage.push(res.data.list[o].message)
        }
        this.unUserListData[l].unMessage = this.unUserListData[l].unMessage.join(',')
      }
    },

    // 获取当前的会话列表
    async getHasBeenConnectedTo() {
      let res = await this.$api.user_list(this.userQuery)
      this.userListData = res.data.list.map(item => {
        item.lastTime = timeDifference(item.lastMessageTime)
        return item
      })
    },
    //搜索icon
    handleIconClick() {
      console.log(1);
    },
    //点击用户
    async getAct(val, index, type) {
      this.isshow = 1;
      // 点击用户切换数据时先清除监听滚动事件，防止出现没有历史数据的用户，滚动条为0，会触发滚动事件
      this.$refs.scrollBox.removeEventListener("scroll", this.srTop);
      //点击变色
      this.act = val.openId;
      //清空消息数组
      this.userInfoList = [];
      this.currentConversationPerson = val
      if (type == '1') {
        let pem = {}
        pem.openId = val.openId
        pem.customerServiceAccount = JSON.parse(localStorage.getItem('accountNumber'))
        // 点击的是待接入的
        let clickRes = await this.$api.user_clickToConnect(pem)
        if (clickRes.code == 200) {
          this.userListData.push(val)
          this.unUserListData.splice(index, 1);
          // 获取当前会话人数据信息
          this.currentConversationPerson = val
          this.currentConversationPerson.isDisplay = 1
        } else {
          this.$message.error(res.msg)
        }
      }

      this.currentSessionPage.page = 1
      this.currentSessionPage.more = true
      // 获取聊天记录
      this.currentSessionPage.openId = val.openId
      let res = await this.$api.information_list(this.currentSessionPage)
      this.userInfoList = res.data.list
      this.userInfoList = this.userInfoList.reverse()
      val.unreadQuantity = 0

      // 直接调用不生效：因为你历史数据刚给，渲染的时候盒子高度还没有成型，所以直接调用拿不到，用个定时器让他在下一轮循环中调用，盒子就已经生成了
      this.$nextTick(() => { // 一定要用nextTick
        // this.setPageScrollTo();
        //页面滚动条距离顶部高度等于这个盒子的高度
        this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
        this.$forceUpdate()
      })

    },
    // 模糊搜索用户
    inquire() {
      // let fuzzy = this.search;
      // if (fuzzy) {
      //   this.userListData = this.userListDatas.filter((item) => {
      //     return item.username.includes(fuzzy);
      //   });
      // } else {
      //   this.userListData = this.userListDatas;
      // }
    },
    //发送
    setUp() {

      let jsonObj = JSON.stringify({
        message: this.textarea,
        customerServiceAccount: JSON.parse(localStorage.getItem('accountNumber')),
        openId: this.currentConversationPerson.openId
      });
      this.websocketsend(jsonObj);

      this.userInfoList.push({ message: this.textarea })

      console.log("发送内容:", this.textarea);
      this.textarea = "";
      // 页面滚动到底部
      this.$nextTick(() => { // 一定要用nextTick
        this.setPageScrollTo();
        //页面滚动条距离顶部高度等于这个盒子的高度
        this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
      })
    },
    // 监听键盘回车阻止换行并发送
    handlePushKeyword(event) {
      console.log(event);
      if (event.keyCode === 13) {
        event.preventDefault(); // 阻止浏览器默认换行操作
        this.setUp(); //发送文本
        return false;
      }
    },
    // 监听按的是ctrl + 回车，就换行
    lineFeed() {
      console.log("换行");
      this.textarea = this.textarea + "\n";
    },
    //点击icon
    extend(val) {
      this.$message.success("你点击了：" + val)
    },
    //滚动条默认滚动到最底部
    setPageScrollTo(s, c) {
      //获取中间内容盒子的可见区域高度
      this.scrollTop = document.querySelector("#box").offsetHeight;
      setTimeout((res) => {
        //加个定时器，防止上面高度没获取到，再获取一遍。
        if (this.scrollTop != this.$refs.scrollBox.offsetHeight) {
          this.scrollTop = document.querySelector("#box").offsetHeight;
        }
      }, 100);
      //scrollTop：滚动条距离顶部的距离。
      //把上面获取到的高度座位距离，把滚动条顶到最底部
      this.$refs.scrollBox.scrollTop = this.scrollTop;
      //判断是否有滚动条,有滚动条就创建一个监听滚动事件，滚动到顶部触发srTop方法
      if (this.$refs.scrollBox.scrollTop > 0) {
        this.$refs.scrollBox.addEventListener("scroll", this.srTop);
      }
    },
    //滚动条到达顶部
    async srTop() {
      //判断：当滚动条距离顶部为0时代表滚动到顶部了
      if (this.$refs.scrollBox.scrollTop == 0) {
        //逻辑简介：
        //到顶部后请求后端的方法，获取第二页的聊天记录，然后插入到现在的聊天数据前面。
        //如何插入前面：可以先把获取的数据保存在 A 变量内，然后 this.userInfoList=A.concat(this.userInfoList)把数组合并进来就可以了

        //拿聊天记录逻辑:
        //第一次调用一个请求拉历史聊天记录，发请求时参数带上页数 1 传过去，拿到的就是第一页的聊天记录，比如一次拿20条。你显示出来
        //然后向上滚动到顶部时，触发新的请求，在请求中把分页数先 +1 然后再请求，这就拿到了第二页数据，然后通过concat合并数组插入进前面，依次类推，功能完成！
        if (this.currentSessionPage.more) {
          this.currentSessionPage.page++
          let res = await this.$api.information_list(this.currentSessionPage)
          res.data.list = res.data.list.reverse()
          this.userInfoList = [...this.userInfoList, ...res.data.list]
          if (this.currentSessionPage.limit == res.data.list.length) {
            this.currentSessionPage.more = true
          } else {
            this.currentSessionPage.more = false
          }
        }
      }
    },

    //-----------------------以下是websocket部分方法

    //数据发送
    websocketsend(Data) {
      this.ws.send(Data)
      console.log(`数据发送` + Data);
    },
    // 连接soket待接入的
    connectToBeConnected() {
      let path = this.path + '/' + null
      this.toBeAccessed = new WebSocket(path); //实例

      //监听链接成功
      this.toBeAccessed.onopen = (val) => {

        console.log(val, '成功');
      }
      //监听后台返回消息
      this.toBeAccessed.onmessage = async (val) => {
        let obj = JSON.parse(val.data)
        console.log(obj);
        let found = false;
        for (let i = 0; i < this.unUserListData.length; i++) {
          if (this.unUserListData[i].openId == obj.openId) {
            // 将 obj 替换为 data 中的对象
            this.unUserListData[i] = obj
            found = true;
            break;// 替换后跳出循环
          }

        }
        // 如果 data 中不存在与 obj 相同 id 的对象，则将 obj 添加到 data 中
        if (!found) {
          this.unUserListData.push(obj);
        }

        let pem = {
          page: 1,
          limit: 3
        }
        for (let l = 0; l < this.unUserListData.length; l++) {
          console.log('开始循环');
          pem.openId = this.unUserListData[l].openId
          console.log('准备调用接口');
          let res = await this.$api.information_list(pem)
          console.log('调用完成');
          this.unUserListData[l].unMessage = []
          for (let o = 0; o < res.data.list.length; o++) {
            this.unUserListData[l].unMessage.push(res.data.list[o].message)
          }
          this.unUserListData[l].unMessage = this.unUserListData[l].unMessage.join(',')
        }
        console.log(this.unUserListData);
        this.$forceUpdate()

      }
      //监听链接关闭
      this.toBeAccessed.onclose = (val) => {
        console.log(val, '监听链接关闭');
      }
      //监听链接异常
      this.toBeAccessed.onerror = (val) => {
        console.log(val, '监听链接异常');
      }
    },
    // 初始化websocket链接
    initWebpack() {
      if (typeof WebSocket === "undefined") {
        this.$message.error('您的浏览器不支持socket')
      } else {
        let accountNumber = localStorage.getItem('accountNumber');

        let path = this.path + '/' + JSON.parse(accountNumber)

        this.ws = new WebSocket(path); //实例

        this.ws.onopen = this.onopen; //监听链接成功
        this.ws.onmessage = this.onmessage; //监听后台返回消息
        this.ws.onclose = this.onclose; //监听链接关闭
        this.ws.onerror = this.onerror; //监听链接异常
      }
    },
    //重新连接
    reconnect() {
      var that = this;
      if (that.lockReconnect) {
        return;
      }
      that.lockReconnect = true;
      //没连接上会一直重连，设置延迟避免请求过多
      that.timeoutnum && clearTimeout(that.timeoutnum);
      that.timeoutnum = setTimeout(function () {
        that.initWebpack(); //新连接
        that.lockReconnect = false;
      }, 5000);
    },
    //重置心跳
    reset() {
      var that = this;
      clearTimeout(that.timeoutObj); //清除心跳倒计时
      clearTimeout(that.serverTimeoutObj); //清除超时关闭倒计时
      that.start(); //重启心跳
    },
    //开启心跳
    start() {
      var self = this;
      self.timeoutObj && clearTimeout(self.timeoutObj); //心跳倒计时如果有值就清除掉，防止重复
      self.serverTimeoutObj && clearTimeout(self.serverTimeoutObj); //超时关闭倒计时如果有值就清除掉，防止重复
      //然后从新开一个定时器
      self.timeoutObj = setTimeout(function () {
        //这里通过readyState判断链接状态，有四个值，0：正在连接，1：已连接，2：正在断开，3：已经断开或者链接不成功
        if (self.ws.readyState == 1) {
          //如果连接正常，给后天发送一个值，可以自定义，然后后台返回我们一个信息，我们接收到后会触发onmessage方法回调
          // self.ws.send("ping");
        } else {
          //如果检测readyState不等于1那也就代表不处在链接状态，那就是不正常的，那就调用重连方法
          self.reconnect();
        }
        //从新赋值一个超时计时器，这个定时器的作用：当你触发心跳的时候可能会出现一个情况，后台崩了，前台发了个心跳，没有回应，就不会触发onmessage方法
        //所以我们需要在这个心跳发送出去了后，再开一个定时器，用于监控心跳返回的时间，比如10秒，那么10秒内如果后台回我了，触发onmessage方法，自然就会把心跳时间和超时倒计时一起清空掉
        //也就不会触发这个关闭连接，但是如果10秒后还是没有收到回应，那么就会触发关闭连接，而关闭连接方法内又会触发重连方法，循环就走起来了。
        // self.serverTimeoutObj = setTimeout(function () {
        //   //如果超时了就关闭连接
        //   self.ws.close();
        // }, self.timeout);
      }, self.timeout);
    },
    //连接成功
    onopen() {
      console.log("连接成功");
      // if (this.ws.readyState == 1) {
      //   //如果连接正常，给后天发送一个值，可以自定义，然后后台返回我们一个信息，我们接收到后会触发onmessage方法回调
      //   this.ws.send(
      //     "链接上啦！！！"
      //   );
      // }
      // this.start(); //链接成功后开启心跳
    },
    //接受后台信息回调
    onmessage(e) {

      console.log(e, '后台返回的');


      let obj = JSON.parse(e.data)
      obj.type = 1
      this.userInfoList.push(obj)
      /**这里写自己的业务逻辑代码**/
      // 页面滚动到底部
      this.$nextTick(() => { // 一定要用nextTick
        this.setPageScrollTo();
        //页面滚动条距离顶部高度等于这个盒子的高度
        this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
      })
      this.reset(); //收到服务器信息，心跳重置
    },
    //关闭连接回调
    onclose(e) {
      console.log("连接关闭");
    },
    //连接异常回调
    onerror(e) {
      console.log("出现错误");
    },
  },
};
</script>
<style>
.el-collapse-item__header {
  background-color: #f2f2f2;
  box-sizing: border-box;
  padding: 0 10px;
}

.el-collapse-item__wrap {
  background-color: #f2f2f2;
}
</style>

<style scoped lang="scss">
.set {
  padding: 10px;
  color: #fff;
  font-size: 18px;
  cursor: pointer;
}

::v-deep .el-collapse-item__header.is-active {
  border-bottom: 1px solid #ebeef5;
}

::v-deep .el-collapse-item__wrap {
  border: none;
}


.badgeItem {
  position: absolute;
  bottom: 4px;
  right: 10px;
}

.wrap {
  height: 90%;
  width: 90%;
  background-color: #f2f2f2;
  margin: -26px auto;
  transform: translateY(10%);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
  border-radius: 10px;
  box-sizing: border-box;
  overflow: hidden;
}

.titleBox {
  height: 10%;
  width: 100%;
  background-image: linear-gradient(to right, #1e76bc, #69a3d5);
  display: flex;
  align-items: center;
  border-top-right-radius: 10px;
  border-top-left-radius: 10px;
}

.infoBottom {
  height: 30%;
  display: flex;
  flex-direction: column;
}

/* 输入框 */
.infoInput {
  height: 58%;
  width: 100%;
  border: none;
  resize: none;
  padding: 10px;
  box-sizing: border-box;
  background-color: #f2f2f2;
  color: #434343;
}

.fasong {
  height: 30px;
  width: 80px;
  background-color: #e8e8e8;
  text-align: center;
  line-height: 30px;
  border-radius: 4px;
  color: #58df4d;
  margin-top: 1%;
  align-self: flex-end;
  margin-right: 20px;
  cursor: pointer;
}

.infoIcon {
  height: 40px;
  width: 100%;
  display: flex;
  align-items: center;
}

.infoIcon i {
  font-size: 24px;
  color: #676767;
  margin-left: 15px;
  cursor: pointer;
}

/* 头像 */
.head_portrait {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}

.head_portrait2 {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}

.stateBox {
  margin-left: 20px;
  padding: 1px 8px;
  background-color: #fff;
  border-radius: 10px;
  text-align: center;
  cursor: pointer;
}

.stateBox2 {
  margin-left: 20px;
  padding: 1px 8px;
  background-color: #fff;
  border-radius: 10px;
  text-align: center;
  cursor: pointer;
}

.stateBox2:hover {
  background-color: #dcdcdc;
}

/* 在线 */
.state1 {
  display: inline-block;
  height: 10px;
  width: 10px;
  border-radius: 50%;
  background-color: #8ee80e;
}

/* 离线 */
.state2 {
  display: inline-block;
  height: 10px;
  width: 10px;
  border-radius: 50%;
  background-color: #cacaca;
}

/* 忙碌 */
.state3 {
  display: inline-block;
  height: 10px;
  width: 10px;
  border-radius: 50%;
  background-color: #ff8c1e;
}

/* 退出登录 */
.state4 {
  display: inline-block;
  height: 10px;
  width: 10px;
  border-radius: 50%;
  background-color: #7e7e7e;
}

.stateText {
  font-size: 14px;
  margin-left: 5px;
}

/* 列表和信息 */
.infoBox {
  max-height: 90%;
  width: 100%;
  display: flex;
}

/* 用户列表大盒子 */
.userList {
  height: 100%;
  width: 300px;
  border-right: 1px solid #ccc;
  display: flex;
  flex-direction: column;
}

/* 用户列表 */
.userListBox {
  /* flex: 1; */
  width: 100%;
  height: 37rem;
  box-sizing: border-box;
  overflow: auto;
}

/* 信息外层盒子 */
.infoList {
  max-height: 100%;
  box-sizing: border-box;
  position: relative;

  /* overflow: hidden; */
  width: 72%;
}

.userInfos {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin-top: -40px;
}

/* 信息列表 */
.infoTop {
  height: 70%;
  margin-top: 20px;
  width: 100%;
  border-bottom: 1px solid #ccc;
  padding: 10px;
  box-sizing: border-box;
  overflow: auto;
}

/* 对方发的信息样式 */
.chatInfoLeft {
  min-height: 70px;
  margin-left: 10px;
  margin-top: 10px;
  display: flex;
}

.chatLeft {
  margin-left: 15px;
  max-width: 400px;
  /* flex: 1; */
}

.chatLeft .text {
  color: #434343;
  margin-top: 8px;
  background-color: #e3e3e3;
  display: inline-block;
  padding: 6px 10px;
  border-radius: 10px;
  text-align: left;
  /* max-width: 50%; */

  white-space: normal;
  /* 换行显示全部字符 */
  /* word-break: break-all; */
  white-space: pre-wrap;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}

/* 自己发的信息样式 */
.chatInfoRight {
  //height: 70px;
  margin-left: 10px;
  margin-top: 10px;
  display: flex;
  flex-direction: row-reverse;
}

.chatRight {
  margin-right: 15px;
  flex: 1;
  /* 用align-items把元素靠右对齐 */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.chatRight .text {
  color: #434343;
  margin-top: 8px;
  background-color: #95ec69;
  display: inline-block;
  padding: 6px 10px;
  border-radius: 10px;
  max-width: 50%;
  /* 忽略多余的空白，只保留一个空白 */
  white-space: normal;
  /* 换行显示全部字符 */
  word-break: break-all;
  white-space: pre-wrap;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}

.searchBox {
  padding: 4px 2px;
  border-bottom: 1px solid #ededed;
  display: flex;
  align-items: center;
}

.input-with-select {
  width: 80%;
  margin-right: 2%;
}

/* 点击用户变色 */
.userFlexAct {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 70px;
  border-bottom: 1px solid #e8e8e8;
  cursor: pointer;
  background-color: #e8e8e8;
}

/* 用户默认颜色 */
.userFlex {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 70px;
  border-bottom: 1px solid #e8e8e8;
  cursor: pointer;
}

/* 用户名 */
.userName {
  width: 100px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-left: -3px;
}

/* 简略信息 */
.userInfo {
  width: 90%;
  font-size: 14px;
  color: #4b4a4a;
  font-weight: bold;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 3px;
  display: flex;
  align-items: flex-start;

}

/* 滚动条样式 */
::-webkit-scrollbar {
  width: 5px;
  height: 10px;
}

::-webkit-scrollbar-thumb {
  background-color: #dbd9d9;
  border-radius: 3px;
}
</style>